package com.tianji.learning.service.impl;

import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjectUtil;
import com.tianji.common.utils.DateUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.po.PointsRecord;
import com.tianji.learning.domain.vo.PointsStatisticsVO;
import com.tianji.learning.enums.PointsRecordType;
import com.tianji.learning.mapper.PointsRecordMapper;
import com.tianji.learning.service.IPointsRecordService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

import static com.tianji.common.utils.DateUtils.DATE_SUFFIX_FORMATTER;
import static com.tianji.learning.constants.RedisConstants.POINTS_BOARD_KEY_PREFIX;

/**
 * <p>
 * 学习积分记录，每个月底清零 服务实现类
 * </p>
 *
 * @author WFan
 * @since 2025-04-01
 */
@Service
@RequiredArgsConstructor
public class PointsRecordServiceImpl extends ServiceImpl<PointsRecordMapper, PointsRecord> implements IPointsRecordService {

    private final RedisTemplate redisTemplate;

    /**
     * 添加积分
     *
     * @param userId
     * @param points
     * @param type
     */
    @Override
    public void addPoints(Long userId, Integer points, PointsRecordType type) {
        // 1.判断当前积分类型是否有上限
        int maxPoints = type.getMaxPoints();
        LocalDateTime now = LocalDateTime.now();
        if (maxPoints > 0){
            // 2.如果有，判断当天用户已获取的积分总额是否超过每日上限
            LocalDateTime begin = LocalDateTimeUtil.beginOfDay(now);
            LocalDateTime end = LocalDateTimeUtil.endOfDay(now);
            // 2.1 查询用户当天已获取的积分总额
            int currentDayPoints = queryTodayTotalPoints(userId, type, begin, end);
            if (currentDayPoints >= maxPoints){
                return;
            }

            // 2.2处理 已有积分 + 本次积分 > 积分上限的情况，弱国超过，不能直接加
            if (currentDayPoints + points > maxPoints){
                points = maxPoints - currentDayPoints;
            }
        }
        // 3.如果没有，直接添加积分
        PointsRecord record = new PointsRecord();
        record.setUserId(userId);
        record.setType(type);
        record.setPoints(points);
        save(record);

        // 4.redis保存积分
        redisTemplate.opsForZSet().incrementScore(POINTS_BOARD_KEY_PREFIX + now.format(DATE_SUFFIX_FORMATTER), userId, points);
    }

    /**
     * 查询用户今日积分情况
     *
     * @return
     */
    @Override
    public List<PointsStatisticsVO> queryTodayMyPoints() {
        // 1.查询统计当前用户当天获取积分情况
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime begin = DateUtils.getDayStartTime(now);
        LocalDateTime end = DateUtils.getDayEndTime(now);

        List<PointsRecord> list = this.query()
                .select("type,sum(points) as points")
                .eq("user_id", UserContext.getUser())
                .between("create_time", begin, end)
                .groupBy("type")
                .list();

        // 2.循环查询结果，封装Vo集合
        List<PointsStatisticsVO> voList = new ArrayList<>(list.size());
        for (PointsRecord pointsRecord : list) {
            PointsStatisticsVO vo = new PointsStatisticsVO();
            vo.setType(pointsRecord.getType().getDesc());
            vo.setPoints(pointsRecord.getPoints());
            vo.setMaxPoints(pointsRecord.getType().getMaxPoints());
            voList.add(vo);
        }
        return voList;
    }

    /**
     * 查询当天累加积分
     * @param userId
     * @param type
     * @param begin
     * @param end
     * @return
     */
    private int queryTodayTotalPoints(Long userId, PointsRecordType type, LocalDateTime begin, LocalDateTime end) {
        PointsRecord record = this.query()
                .select("sum(points) as points")
                .eq("user_id", userId)
                .eq("type", type)
                .between("create_time", begin, end)
                .one();

        return ObjectUtil.isEmpty(record) || ObjectUtil.isEmpty(record.getPoints()) ? 0 : record.getPoints();
    }

}
